#include <iostream>
#include <iomanip>
#include <cstring>
#include <chrono>
#include <thread>
#include <cerrno>

#include "..\Sources\NahidaBenchmarkTest.h"
#include "..\Sources\NahidaMockTest.h"
#include "..\Sources\NahidaUnitTest.h"

using namespace std;

class Calculator {
public:
	virtual int Add(int a, int b) = 0;
};

class MockCalculator : public Calculator{
public:
	MockMethod(int, Add, (int, int));
};

TESTCASE(BenchmarkTestTest) {
	string val = "3.141592653589793238462643383279502884";
	int count = 1000000;

	auto rt = NahidaProject::NahidaBenchmarkTest::Time(count, {{"atof", [val]() {double d = atof(val.data());}},{"strtod", [val]() {double d = strtod(val.data(), nullptr);}},{"sscanf", [val]() {double d; sscanf(val.data(), "%lf", &d);}},{"stod", [val]() {double d = stod(val, nullptr);}}});

	auto comp = NahidaProject::NahidaBenchmarkTest::Compare(rt);
	NahidaProject::NahidaBenchmarkTest::PrintResult(comp);
	cout << endl << "runtimes in milliseconds:" << endl;
	NahidaProject::NahidaBenchmarkTest::PrintResult<chrono::milliseconds>(comp);
	cout << endl << "runtimes with min, max, mean, variance and standard deviation" << endl;
	cout << endl << "nanoseconds" << endl;
	NahidaProject::NahidaBenchmarkTest::PrintResult<chrono::nanoseconds>(rt);
	cout << endl << "microseconds" << endl;
	NahidaProject::NahidaBenchmarkTest::PrintResult<chrono::microseconds>(rt, 3);
	cout << endl << "milliseconds" << endl;
	NahidaProject::NahidaBenchmarkTest::PrintResult<chrono::milliseconds>(rt, 5);

	if (NahidaProject::NahidaBenchmarkTest::WriteResult("sample", rt))
		cout << endl << "successfully wrote runtimes to sample-*.txt files" << endl << endl;
	else
		cerr << endl << "there were problems: " << strerror(errno) << endl << endl;
}



TESTCASE(MockTestTest){
	MockCalculator calculator;
	When(calculator.Add).Return(3);
	JUDGE_EQUAL(calculator.Add(1, 2), 3);
}


TESTCASE(UnitTestMacroTest) {
	JUDGE_EQUAL(78 + 13, 91);
	JUDGE_NOTEQUAL(1 + 1, 10086);
	JUDGE_TRUE(true);
	JUDGE_FALSE(false);
	JUDGE_THROWEXCEPTION(throw std::runtime_error("This is a test exception"), std::runtime_error);
	JUDGE_TIMEOUT([]() {
		std::this_thread::sleep_for(std::chrono::seconds(3));
	}, 5000);
}



int main(int argc, char **argv){
	NahidaProject::NahidaUnitTest::RunAllTests();
	return 0;
}